package com.dxf.generator;

import com.dxf.database.dbDao.DBMetaData;
import com.dxf.database.dbDao.impl.DBMetaDataImpl;
import com.dxf.database.dbInfo.DataBase;
import com.dxf.database.dbInfo.DataBaseFactory;
import com.dxf.entity.Settings;
import com.dxf.entity.Table;
import com.dxf.utils.FileUtils;
import com.dxf.utils.PropertiesUtils;
import com.dxf.utils.StringUtils;
import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.*;

public class BuildCodeImpl implements BuildCode {

    private String templatePath = new File("").getAbsolutePath() + "\\模板";//模板路径
    private static String outPath = new File("").getAbsolutePath() + "\\生成";//代码生成路径
    private Configuration cfg = new Configuration();    //freeMarker的配置类
    private Settings settings = new Settings();//自己的设置类

    private HashMap<String, Object> dataModel = new HashMap<>();

    private String defaultRegex = "_"; //表中默人用下划线区分单词

    //指定加载的模板
    public BuildCodeImpl(String templatePath) throws IOException {
        this.templatePath = templatePath;
        //指定模板加载器
        FileTemplateLoader templateLoader = new FileTemplateLoader(new File(templatePath));
        this.cfg.setTemplateLoader(templateLoader);
    }


    //扫描模板得到file列表
    @Override
    public List<File> scanTemplateFiles(String fileUrl) throws Exception {
        // 根据模板路径找到次路径下的所有模板文件 递归扫描
        List<File> files = FileUtils.searchAllFile(new File(fileUrl));//所有的模板文件
        return files;
    }

    //得到数据库中的所有数据
    @Override
    public List<Table> getDataInfo(DataBase dataBase) throws Exception {

        DBMetaData dbMetaData = new DBMetaDataImpl();
        List<Table> allTables = dbMetaData.getAllTables(dataBase);

        return allTables;
    }


    //执行代码生成
    public void execute(String outpath, List<Table> tables, List<File> files) throws Exception {
        for (Table table : tables) {
            //得到每一个表的数据和配置文件中的数据
            HashMap<String, Object> dataModel = getDataModel(table);
            for (File file : files) {
                //根据模板和数据生成内容
                executeGeneratorCode(dataModel, file);
            }
        }
    }

    //添加数据元
    @Override
    public void AddModelData(String k, String v) {
        dataModel.put(k, v);
    }


    //获取数据库中的表
    @Override
    public List<Table> getDbByConfig() throws Exception {
        //1 得到dataBase
        DataBase database = DataBaseFactory.createDatabase();
        //2  获取配置文件中需要的表
        Map<String, String> dbMap = PropertiesUtils.userMap;
        String[] tableNames = dbMap.get("tableName").split(",");
        Optional<String> any = Arrays.stream(tableNames).filter(x -> "all".equals(x)).findAny();
        DBMetaData dbMetaData = new DBMetaDataImpl();
        List<Table> tableList = new ArrayList<>();
        if (any.isPresent()) {
            tableList = dbMetaData.getAllTables(database);
        } else {
            for (String tableName : tableNames) {
                List<Table> tables = dbMetaData.getTable(database, tableName);
                tableList.addAll(tables);
            }

        }

        return tableList;
    }


    /**
     * 对模板进行文件生成
     *
     * @param dataModel 数据模型
     * @param file      模板文件
     *                  <p>
     *                  模板文件:
     *                  c:com.dxf.system.abc.java
     */
    public void executeGeneratorCode(Map<String, Object> dataModel, File file) throws Exception {
        //1 文件路径处理
        //绝对路径,如 C:\模板\${path1}\${path2}\${path3}\${ClassName}.java
        //this.templatePath : C:\模板\
        String templateFileName = file.getAbsolutePath().replace(this.templatePath, "");// 具体某一个模板下的文件所在的绝对路径减去总的模板路径
        //templateFileName    ${path1}\${path2}\${path3}\${ClassName}.java
        String outFileName = processTemplateString(templateFileName, dataModel);//生成文件名字
        //2 读取文件模板
        Template template = cfg.getTemplate(templateFileName);
        template.setOutputEncoding("utf-8");//指定生成文件字符集编码
        //3 创建新的文件
        File file1 = FileUtils.mkdir(outPath, outFileName);

        //4 文件生成
        FileWriter fw = new FileWriter(file1);
        template.process(dataModel, fw);
        fw.close();

    }


    /**
     * 把dataModel 中的settings.path1 settings.path2  settings.path3 等几个变量在temlateString 中替换掉
     *
     * @param temlateString ${path1}\${path2}\${path3}\${ClassName}.java
     * @param dataModel     settings.path1 settings.path2  settings.path3
     * @return com.example.demo.xxx.java
     * @throws Exception
     */
    public String processTemplateString(String temlateString, Map dataModel) throws Exception {
        StringWriter out = new StringWriter();
        Template template = new Template("ts", new StringReader(temlateString), cfg);
        // 处理模板
        template.process(dataModel, out);
        return out.toString();
    }


    //生成数据模型
    private HashMap<String, Object> getDataModel(Table table) {
        /**
         * 2 准备数据模型
         *       自定义配置
         *       元数据
         *       setting    就前面手动写的文件名,输出路径等等
         */
        //  HashMap<String, Object> dataModel = new HashMap<>();
        //自定义配置
        dataModel.putAll(PropertiesUtils.applicationMap);//把配置文件信息加载到模型中,主要是sql类型和java类型对象上,如 varchar 对应String
        //元数据
        dataModel.put("table", table);//table.类名(name2)
        //setting
        // dataModel.put("settings",this.settings.getSettingMap());
        dataModel.putAll(this.settings.getSettingMap());

        //表名 多个单词用 什么分割(如下划线)
        if (PropertiesUtils.dbMap.get("tableRegex") != null) {
            defaultRegex = PropertiesUtils.dbMap.get("tableRegex");
        }

        // 类型
        dataModel.put("ClassName", table.getName2());

        String lowFirst = getLowFirst(table.getName2());


        dataModel.put("className", lowFirst);
        dataModel.put("TableName", table.getName());//表名称

        dataModel.put("TableNameUpper", StringUtils.getUpper(table.getName(), defaultRegex));
        dataModel.put("userMap", PropertiesUtils.userMap);//用户map

        return dataModel;

    }

    /**
     * 首字母转小写
     *
     * @param name
     * @return
     */
    private String getLowFirst(String name) {


        char[] chars = name.toCharArray();
        char aChar = Character.toLowerCase(chars[0]);
        chars[0] = aChar;
        return String.valueOf(chars);
    }

}
